package dbop

import (
	"database/sql"
	"fmt"
	"log"
	"reflect"

	_ "github.com/go-sql-driver/mysql"
)

var SqlCmdIndex = [...]string{
	"sqlcmd_connect_database",
	"sqlcmd_check_table_exist",
	"sqlcmd_create_well_cover_main_table",
	"sqlcmd_check_item_exsit",
	"sqlcmd_check_item_data",
}

var SqlOpCmdFormatList = map[string](string){
	"sqlcmd_connect_database":          "%s:%s@tcp(%s:%s)/%s",
	"sqlcmd_check_table_exist":         "select count(*) from information_schema.tables where table_name='%s';",
	"sqlcmd_create_table":              "create table ",
	"sqlcmd_check_item_exsit":          "select * from %s where %s='%s';",
	"sqlcmd_check_item_data":           "select * from %s where %s='%s';",
	"sqlcmd_insert_data_to_main_table": "insert into %s ",
	"sqlcmd_update_items_with_key":     "update %s ",
	"sqlcmd_delete_table":              "drop table %s",
	"sqlcmd_delete_item":               "delete from ",
}

// An InvalidParameterError describes an invalid argument passed to MySQL items.
type InvalidParameterError struct {
	Type reflect.Type
}

func (e InvalidParameterError) Error() string {
	if e.Type == nil {
		return "SQL: Unmarshal(nil)"
	}

	if e.Type.Kind() != reflect.Struct {
		return "SQL: Unmarshal(non-struct " + e.Type.String() + ")"
	}
	return "SQL: Unmarshal(nil " + e.Type.String() + ")"
}

/*===========连接数据库函数===========*/
//  @brief   连接数据库
//  @param   userName:     登录数据库的用户名
//  @param   password:     登录数据库的密码
//  @param   hostName:     数据库的主机名
//  @param   port:         数据库的端口号
//  @param   databaseName: Database名称
//  @return  db:           *sql.DB,数据库对象
//  @return  err:          错误码
// /
func ConnectSQL(userName string, password string, hostName string, port string, databaseName string) (db *sql.DB, err error) {
	conStr := fmt.Sprintf(SqlOpCmdFormatList["sqlcmd_connect_database"], userName, password, hostName, port, databaseName)
	db, err = sql.Open("mysql", conStr)
	if err != nil {
		log.Fatal(err.Error())
		return nil, err
	}
	return db, err
}

/*===========查找表是否存在===========*/
//  @brief   MySQL查询一张数据表Table
//  @param   tableName:    操作的表名称
//  @param   db:           指向的数据库
//  @return  tableCount:   存在的数据表的数量，为1
//  @return  err:          错误码
// /
func CheckTablesCreated(tableName string, db *sql.DB) (tableCount int, err error) {
	execCmd := fmt.Sprintf(SqlOpCmdFormatList["sqlcmd_check_table_exist"], tableName)
	sqlResult, err := db.Query(execCmd)
	if err != nil {
		log.Fatal(err.Error())
		return 0, err
	}
	for sqlResult.Next() {
		err = sqlResult.Scan(&tableCount)
		if err != nil {
			log.Println("Error to excuate:" + execCmd)
			return 0, err
		}
	}

	return tableCount, err
}

/*=============创建数据表=============*/
//  @brief   MySQL查询一张数据表Table
//  @param   tableName:    操作的表名称
//  @param   db:           指向的数据库
//  @param   item:         空接口传入带标签的结构体
//  @param   string:       创捷的表名称
//  @param   error:        执行的错误码
//  ===========执行结果============
//  string    |   error    |  执行结果
//-------------------------------------
//  "nil"     |   !nil     |  执行失败
//  "nil"     |   nil      |  数据表已经存在
//  数据表名  |   nil      |  创建成功
// /
func CreateTable(tableName string, db *sql.DB, item interface{}) (string, error) {
	resultCount, err := CheckTablesCreated(tableName, db)
	if err != nil {
		log.Println(err.Error())
		return "nil", err
	}
	if resultCount == 0 {
		execCmd := SqlOpCmdFormatList["sqlcmd_create_table"] + tableName

		rv := reflect.ValueOf(item)
		s := rv.Elem()
		if rv.Kind() != reflect.Ptr || rv.IsNil() {
			return "nil", &InvalidParameterError{reflect.TypeOf(item)}
		}

		//将结构体标签加入SQL语句中
		execCmd = execCmd + "("
		for i := 0; i < s.NumField(); i++ {
			execCmd = execCmd + rv.Type().Elem().Field(i).Tag.Get("index_tag")
			execCmd = execCmd + " "
			execCmd = execCmd + rv.Type().Elem().Field(i).Tag.Get("property_tag")
			if i < s.NumField()-1 {
				execCmd = execCmd + ","
			}

		}
		execCmd = execCmd + ")ENGINE=InnoDB DEFAULT CHARSET=utf8;"
		_, err = db.Exec(execCmd)
		if err != nil {
			log.Println(err.Error())
			return "nil", err
		} else {
			return tableName, nil
		}
	} else {
		return "nil", nil
	}
}

/*=============删除数据表=============*/
//  @brief   MySQL删除一张数据表Table
//  @param   tableName:    操作的表名称
//  @param   db:           指向的数据库
//  @param   string:       创捷的表名称
//  @param   error:        执行的错误码
//  ===========执行结果============
//  string    |   error    |  执行结果
//-------------------------------------
//  "nil"     |   !nil     |  执行失败
//  "nil"     |   nil      |  数据表不存在
//  数据表名  |   nil      |  删除成功
// /
func DeleteTable(tableName string, db *sql.DB) (string, error) {
	resultCount, err := CheckTablesCreated(tableName, db)
	if err != nil {
		log.Println(err.Error())
		return "nil", err
	}
	if resultCount == 0 {
		return "nil", nil
	} else {
		execCmd := fmt.Sprintf(SqlOpCmdFormatList["sqlcmd_delete_table"], tableName)
		_, err = db.Exec(execCmd)
		if err != nil {
			log.Println(err.Error())
			return "nil", err
		} else {
			return tableName, nil
		}
	}
}

/*=============数据写入=============*/
//  @brief   MySQL查询某一项数据
//  @param   tableName:    操作的表名称
//  @param   db:           指向的数据库
//  @param   checkKeyWord: 数据库中的索引
//  @param   checkValue:   某一项数据中索引所对应的值
//  @retrun  err:          error code
//  @return  *sql.Rows:    检索到的结果指针
// /
func CheckItemData(tableName string, db *sql.DB, checkKeyWord string, checkValue string) (*sql.Rows, error) {
	execCmd := fmt.Sprintf(SqlOpCmdFormatList["sqlcmd_check_item_data"], tableName, checkKeyWord, checkValue)
	sqlResult, err := db.Query(execCmd)
	if err != nil {
		log.Println(err.Error())
		return sqlResult, err
	}
	return sqlResult, nil
}

/*=========数据查询是否存在=========*/
//  @brief   查询某一项是否存在
//  @param   tableName:    操作的表名称
//  @param   db:           指向的数据库
//  @param   checkKeyWord: 数据库中的索引
//  @param   checkValue:   某一项数据中索引所对应的值
//  @return  itemNum:      查询到的结果条数
//  @return  error:
// /
func CheckItemDataExsit(tableName string, db *sql.DB, checkKeyWord string, checkValue string) (itemNum int, err error) {
	execCmd := fmt.Sprintf(SqlOpCmdFormatList["sqlcmd_check_item_exsit"], tableName, checkKeyWord, checkValue)
	sqlResult, err := db.Query(execCmd)
	if err != nil {
		log.Fatal(err.Error())
		return 0, err
	}
	itemNum = 0
	for sqlResult.Next() {
		itemNum++
	}
	return itemNum, err
}

/*=============数据写入=============*/
// @brief   添加数据
// @param   tableName: 操作的表名称
// @param   db:        指向的数据库
// @param   item:      空接口来接收传入的结构体
// @return  error:
///
func AddItemToDatabase(tableName string, db *sql.DB, item interface{}) error {
	execCmd := fmt.Sprintf(SqlOpCmdFormatList["sqlcmd_insert_data_to_main_table"], tableName)

	rv := reflect.ValueOf(item)
	s := rv.Elem()
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		return &InvalidParameterError{reflect.TypeOf(item)}
	}

	//将结构体标签加入SQL语句中
	execCmd = execCmd + "("
	for i := 0; i < s.NumField(); i++ {
		execCmd = execCmd + rv.Type().Elem().Field(i).Tag.Get("index_tag")
		if i < s.NumField()-1 {
			execCmd = execCmd + ","
		}
	}
	execCmd = execCmd + ")"
	execCmd = execCmd + "values"
	//将结构体变量传入的值加入SQL语句中
	execCmd = execCmd + "("

	for i := 0; i < s.NumField(); i++ {
		valString := ""
		if s.Field(i).Kind() == reflect.String {
			valString = fmt.Sprintf("'%v'", s.Field(i).Interface())
		} else {
			valString = fmt.Sprintf("%v", s.Field(i).Interface())
		}

		execCmd = execCmd + valString
		if i < s.NumField()-1 {
			execCmd = execCmd + ","
		}

	}
	execCmd = execCmd + ");"

	//执行SQL语句
	_, err := db.Exec(execCmd)
	if err != nil {
		log.Println(err.Error())
		return err
	}
	return nil
}

/*=============数据更新=============*/
// @brief   更新数据
// @param   tableName: 操作的表名称
// @param   db:        指向的数据库
// @param   item:      空接口来接收传入的结构体
// @param   key:       用来查找已存在在表中的数据项的索引
// @param   value:     对应key需要满足的条件
// @return  error:
///
func UpdateItemToDatabase(tableName string, db *sql.DB, item interface{}, key string, value string) error {
	execCmd := fmt.Sprintf(SqlOpCmdFormatList["sqlcmd_update_items_with_key"], tableName)
	rv := reflect.ValueOf(item)
	s := rv.Elem()
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		return &InvalidParameterError{reflect.TypeOf(item)}
	}

	//将结构体标签加入SQL语句中
	execCmd = execCmd + "set "
	for i := 0; i < s.NumField(); i++ {
		// valString := fmt.Sprintf("%v", s.Field(i).Interface())
		var valString string
		if s.Field(i).Kind() == reflect.String {
			valString = fmt.Sprintf("'%v'", s.Field(i).Interface())
		} else {
			valString = fmt.Sprintf("%v", s.Field(i).Interface())
		}
		execCmd = execCmd + rv.Type().Elem().Field(i).Tag.Get("index_tag") + "=" + valString
		if i < s.NumField()-1 {
			execCmd = execCmd + ","
		}
	}

	execCmd = execCmd + " where " + key + "=" + value + ";"
	//执行SQL语句
	_, err := db.Exec(execCmd)
	if err != nil {
		log.Println(err.Error())
		return err
	}
	return err
}

/*=============数据删除=============*/
// @brief   删除数据
// @param   tableName:   操作的表名称
// @param   db:          指向的数据库
// @param   item:        空接口来接收传入的结构体
// @param   checkKeyWord:用来查找已存在在表中的数据项的索引
// @param   checkValue:  对应key需要满足的条件
// @return  error:
//  ===========执行结果============
//  string    |   error    |  执行结果
//-------------------------------------
//  "nil"     |   !nil     |  执行失败
//  "nil"     |   nil      |  数据项不存在
//  关键词名  |   nil      |  删除成功
// /
///
func DeleteItemFromDatabase(tableName string, db *sql.DB, checkKeyWord string, checkValue string) (string, error) {
	itemNum, _ := CheckItemDataExsit(tableName, db, checkKeyWord, checkValue)
	if itemNum != 0 {
		execCmd := SqlOpCmdFormatList["sqlcmd_delete_item"] + tableName + " where " + checkKeyWord + "=" + "'" + checkValue + "'" + ";"
		_, err := db.Exec(execCmd)
		if err != nil {
			log.Println(err.Error())
			return "nil", err
		}
		return checkKeyWord, nil
	} else {
		return "nil", nil
	}
}

//4.2 如果ID已经存在，则进行数据更新

/*===========历史记录表操作=========*/
